package org.hydrakyoufeng.stuplatform.utils;

import org.hydrakyoufeng.stuplatform.exception.ProcessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

/**
 * 图形验证码工具
 * @author HydraKyouFeng
 *
 */
@Component
public class VerificationCodeUtils {

	/**
	 * 字体大小
	 */
	private int fontSize = 20;

	private Random random = new Random();

	/**
	 * 用于生成验证码的种子字符串
	 */
	private String codeSeed = "123456789abcdefghjkmnpqrstuvwxyz";

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 生成图形验证码的图片
	 * @param str 需要生成验证码的字符串
	 * @return
	 */
	public RenderedImage createCodeImage(String str) {
		int width = str.length() * fontSize + 4;
		int height = fontSize + 6;
		BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
		
		Font mFont = new Font("Arial", Font.TRUETYPE_FONT, fontSize);
		Graphics g = image.getGraphics();
		Random rd = new Random();
		// 设置背景颜色
		g.setColor(new Color(rd.nextInt(55) + 200, rd.nextInt(55) + 200, rd.nextInt(55) + 200));g.fillRect(0, 0, width, height);
		// 设置字体
		g.setFont(mFont);
		// 画边框
//		g.setColor(Color.black);
//		g.drawRect(0, 0, width - 1, height - 1);

		// 画验证码
		for (int i = 0; i < str.length(); i++) {
			g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd.nextInt(200)));
			g.drawString(str.charAt(i) + "",2 + 20 * i + rd.nextInt(8) - 4, rd.nextInt(6 * 2) + fontSize - 6);
		}
		// 随机产生2个干扰线
		for (int i = 0; i < 2; i++) {
			g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd.nextInt(200)));
			int x1 = rd.nextInt(5);
			int x2 = width - rd.nextInt(5);
			int y1 = 2 + rd.nextInt(height - 4);
			int y2 = 2 + rd.nextInt(height - 4);
			g.drawLine(x1, y1, x2, y2);
		}
		g.dispose();
		return image;
	}
	
	/**
	 * 将图片以jpeg的方式输出到输出流上
	 * @param image
	 * @throws IOException 
	 */
	public void writeToStream(RenderedImage image,OutputStream os) throws IOException {
		ImageIO.write(image, "jpeg", os);
	}

	public String getCodeImageBase64(String str) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
		RenderedImage image = createCodeImage(str);
		writeToStream(image,baos);
		Base64.Encoder encoder = Base64.getEncoder();
		// 对图片的二进制数据进行base64编码
		String base64 = encoder.encodeToString(baos.toByteArray());
		return "data:image/jpeg;base64," + base64;
	}

	/**
	 * 生成4位数的随机验证码
	 * @return
	 */
	public String getRandomCode() {

		StringBuilder sb = new StringBuilder();
		for (int i = 0;i < 4;i ++) {
			int r = random.nextInt(codeSeed.length());
			char c = codeSeed.charAt(r);
			// 如果生成的是字母，随机变为大写或小写
			if (r >= 9) {
				r = random.nextInt(10);
				if (r >= 5) {
					sb.append(c);
				} else {
					sb.append(Character.toUpperCase(c));
				}
			} else {
				sb.append(c);
			}


		}
		return sb.toString();
	}

	/**
	 * 对外可以直接提供现成逻辑的方法
	 * 生成随机验证码字符串，并根据字符串生成Base64的图，最后将验证码的值存储到redis中
	 * @return base64编码的图形验证码和uuid的值
	 */

	public Map<?,?> getAndSave() throws Exception {
		// 生成验证码
		String code = getRandomCode();
		// 生成UUID
		String uuid = UUID.randomUUID().toString();
		// 将验证码的值保存到redis中（保存小写字母方便比较）：验证码有效期5分钟。
		redisTemplate.opsForValue().set(uuid,code, 5, TimeUnit.MINUTES);
		// 生成base64的验证码图片
		String base64 = getCodeImageBase64(code);
		return Map.of("codeImage", base64, "uuid", uuid);
	}

	/**
	 * 从redis中校验图形验证码是否正确
	 * @param uuid
	 * @param code
	 * @throws Exception
	 */

	public void valideteCode(String uuid,String code) throws Exception {
		// 从redis中获取验证码
		String codeFromRedis = (String) redisTemplate.opsForValue().get(uuid);
		// 判断验证码是否过期
		if (codeFromRedis == null) {
			throw new ProcessException("验证码已经失效");
		}
		// 无论成功与否，都删除验证码，避免使用同样的验证码，导致绕过验证码
		redisTemplate.delete(uuid);
		// 判断验证码是否正确
		if (codeFromRedis.equalsIgnoreCase(code)) {
			return;
		}
		throw new ProcessException("验证码错误");
	}
}
